#ifndef NETWORK_LIBEVENT_H__
#define NETWORK_LIBEVENT_H__

#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <queue>
#include "single_producer_consumer.hpp"
#include "memory_pool.hpp"
#include "thread.h"

namespace engine
{
	class LibEvent;
	struct Message;

	struct Address 
	{
		uint32 host; 
		uint16 port;
	};

	struct Connection
	{
		Address	address;

		LibEvent* libevent;

		struct bufferevent* buffevt;

		bool connected;

		void* bind;

		void* buff;
		
		uint16 buffsize;
		
		uint16 buffpos;
	};

	struct Command
	{
		enum Type
		{
			Connect,
			Disconnect,
			Send,
		};

		union Data
		{
			Address addr;
			Connection* conn;

			struct SendData
			{
				Connection* conn;
				void* buff;
				uint16 size;
			} 
			send;
		};

		Type type;
		Data data;
	};

	struct Message
	{
		enum Type
		{
			ConnectionMessage,
			ConnectionAccepted,
			ConnectionFailed,
			ConnectionBreak,
			ConnectionSuccess,
		};

		Type type;
		Connection* conn;
		void* buff;
		uint16 size;
	};

	template<uint16 LEN>
	struct Buffer
	{
		uint8 buff[LEN];
	};

	typedef Buffer<0xFF>	BufferFF;
	typedef Buffer<0xFFF>	BufferFFF;
	typedef Buffer<0xFFFF>	BufferFFFF;

	class LibEvent : protected Thread
	{
	public:
		virtual void* entry();

	public:
		LibEvent();

		bool start(const char* host, uint16 port, uint32 maxConnections);
		
		void stop();

		bool connect(const char* host, uint16 port);

		void disconnect(Connection* conn);

		void send(void* data, uint16 size, Connection* conn);

		Message* lockMessage();

		void unlockMessage();
		
		void freeBuffer(void* buff, uint32 size);
		
		void freeConnection(Connection* conn);

		void bindData(Connection* conn, void* data);

		void* bindData(Connection* conn);

		inline bool isAlive() const
		{
			return Thread::isAlive();
		}

	protected:
		void processAccept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* sin, int len);
		void processRead(struct bufferevent* buffevt, void* ctx);
		void processEvent(struct bufferevent* buffevt, short what, void* ctx);

		void processConnect(const Address& addr);
		void processDisconnect(Connection* conn);

		void pushMessage(Message::Type type, Connection* conn = NULL, void* buff = NULL, uint16 size = 0);
		
		void* allocBuffer(uint32 size);

		Connection* allocConnection();

		void processCommands();
		void dispatchEvents();

	public:
		static void EVCONNLISTENER_CALLBACK(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* sin, int len, void* ctx);
		static void BUFFEREVENT_READ_CALLBACK(struct bufferevent* buffevt, void* ctx);
		static void BUFFEREVENT_WRITE_CALLBACK(struct bufferevent* buffevt, void* ctx);
		static void BUFFEREVENT_EVENT_CALLBACK(struct bufferevent* buffevt, short what, void* ctx);

	private:
		Address mAddress;
		uint32 mMaxConnections;
		volatile bool mStopThread;
		volatile bool mEnterLoop;
		volatile bool mStartSucc;
		event_base* mEventBase;
		evconnlistener* mListener;

		CriticalSection mConnectionPoolCS;
		MemoryPool<Connection> mConnectionPool;
		uint32 mConnectionCount;

		SingleProducerConsumer<Command> mCommands; 
		SingleProducerConsumer<Message> mMessages; 

		CriticalSection mBuffPoolCS;
		MemoryPool<BufferFF> mBuffPoolFF;
		MemoryPool<BufferFFF> mBuffPoolFFF;
		MemoryPool<BufferFFFF> mBuffPoolFFFF;
	};
}

#endif